from StrategyEngine.TradingAlgorithm import TradingAlgorithm
from StrategyEngine.SimulationParameters import SimulationParameters
from StrategyEngine.TradingCalender import GetCalender
from StrategyEngine.TradingEnvironment import TradingEnvironment
import Analysis.General as General
import StrategyEngine
import Core.Gadget as Gadget
import datetime
import pandas as pd
import numpy as np

# ---Display Ajustment---
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)

# ---Connecting DataBase---
from Core.Config import *
config = Config()
database = config.DataBase("MySQL")
realtime = config.RealTime(db=0)

# ---Defining some TimePeriod---
onemonth = datetime.timedelta(days=31)
oneseason = datetime.timedelta(days=90)
oneyear = datetime.timedelta(days=365)
oneday = datetime.timedelta(days=1)
fifteenhours = datetime.timedelta(hours=15)

# ---Transformation of Date Form---
def StrtoDataTime(ob):
    if isinstance(ob, list):
        for i in range(len(ob)):
            ob[i] = datetime.datetime.strptime(ob[i], "%Y-%m-%d")
    elif isinstance(ob, str):
        ob = datetime.datetime.strptime(ob, "%Y-%m-%d")
    return ob
def StrfromDataTime(ob):
    if isinstance(ob, list):
        for i in range(len(ob)):
            ob[i] = datetime.datetime.strftime(ob[i], "%Y-%m-%d")
    else:
        ob = datetime.datetime.strftime(ob, "%Y-%m-%d")
    return ob

# ---Stocks selected by PB---

def SelectbyPB(date):

    factorName = "PB_LF"
    factors = [factorName]
    df_PB = General.Profile(database, date, factors, instruments=None)
    df_PB = df_PB[df_PB[factorName] > 0]
    df_PB.sort_values(by=factorName, ascending=True, inplace=True)
    count = int(0.2 * len(df_PB))
    df_PB = df_PB.iloc[:count]

    return df_PB

# ---Find Data---

def InitialData(date):

    #---Stocks selected by PB---
    selected_stock_byPB = SelectbyPB(date)
    print('---selected_stocks_by_PB---\n', selected_stock_byPB)
    stocklist = selected_stock_byPB['Symbol'].values

    #---Find Data from "factors"---
    def FindData(dt,name):
        #
        factorName = name
        factors = [factorName]
        df = General.Profile(database, dt, factors, instruments=None)
        df = df[df['Symbol'].isin(stocklist)]
        df.drop_duplicates(inplace=True)
        #
        return df

    #---Find Data from "fundamental"---
    def FindDatafromFundamental(date, name):

        # ---the Time range of ReleaseDate---
        releasedatelist = [date]
        for i in range(30):
            releasedatelist.append(date - i * oneday)
        releasedatelist = StrfromDataTime(releasedatelist)

        # ---the Time range of ReportDate---
        reportdate = date - 25 * oneday
        reportdatelist = []
        for i in range(30):
            reportdatelist.append(reportdate - i * oneday)
        reportdatelist = StrfromDataTime(reportdatelist)

        sql = "SELECT * FROM stock.fundamental WHERE Symbol in {} AND ReleaseDate in {} and ReportDate in {}".format(
            tuple(stocklist), tuple(releasedatelist), tuple(reportdatelist))
        #print('---sql_command---\n', sql)

        data = []
        statements = database.FindWithSQL("stock", "", sql)
        for statement in statements:
            symbol = statement["Symbol"]
            #DateTime = statement['DateTime']
            df = statement[name]
            data.append([symbol, df])
        #
        df = pd.DataFrame(data, columns=["Symbol", name])
        return df


    # ---Select Data from "Factors"---

    ROA = FindData(date, 'ROA_OperatingProfit1_LYR')  #1
    ROA_lastyear = FindData(date-oneyear, 'ROA_OperatingProfit1_LYR')
    ROA_lastyear.columns = ['Symbol','ROA_OperatingProfit1_LYR_lastyear']

    #PE_NetIncome2_LYR = FindData(date, 'PE_NetIncome2_LYR')

    CurrentRatio = FindData(date, 'CurrentRatio')
    CurrentRatio_lastyear = FindData(date-oneyear, 'CurrentRatio')
    CurrentRatio_lastyear.columns = ['Symbol', 'CurrentRatio_lastyear']

    Growth_ProfitMargin = FindData(date, 'Growth_CAGR_ProfitMargin_OperatingProfit1_1Yr')   #8

    AssetTurnover_LYR = FindData(date, "AssetTurnover_LYR")
    AssetTurnover_LYR_lastyear = FindData(date-oneyear, "AssetTurnover_LYR")
    AssetTurnover_LYR_lastyear.columns = ['Symbol','AssetTurnover_LYR_lastyear']

    OperatingCashFlow = FindData(date, 'PCF_LYR')  # 3
    PE_NetIncome2_LYR = FindData(date, 'PE_NetIncome2_LYR')


    # ---Select Data from "Fundamental"---

    #OperatingProfit1 = FindDatafromFundamental(date, 'OperatingProfit1')  #1

    TotalShares = FindDatafromFundamental(date, 'TotalShares')
    TotalShares_lastyear = FindDatafromFundamental(date-oneyear, 'TotalShares')
    TotalShares_lastyear.columns = ['Symbol', 'TotalShares_lastyear']

    OCF = FindDatafromFundamental(date, 'OperatingCashFlow') #3
    OCF_lastyear = FindDatafromFundamental(date-oneyear, 'OperatingCashFlow')
    OCF_lastyear.columns = ['Symbol','OperatingCashFlow_lastyear']

    LTDebt = FindDatafromFundamental(date, 'LTDebt')
    LTDebt_lastyear = FindDatafromFundamental(date-oneyear, 'LTDebt')
    LTDebt_lastyear.columns = ['Symbol', 'LTDebt_lastyear']

    TotalAsset = FindDatafromFundamental(date, 'TotalAsset')
    TotalAsset_lastyear = FindDatafromFundamental(date-oneyear, 'TotalAsset')
    TotalAsset_lastyear.columns = ['Symbol', 'TotalAsset_lastyear']

    EBITDA = FindDatafromFundamental(date, 'EBITDA')


    # ---Data Merge---

    df = pd.DataFrame(columns=['Symbol'])
    factorlist = [ROA, ROA_lastyear, OCF, OCF_lastyear,OperatingCashFlow, PE_NetIncome2_LYR,LTDebt, LTDebt_lastyear, TotalAsset, TotalAsset_lastyear,
                  CurrentRatio, CurrentRatio_lastyear, TotalShares, TotalShares_lastyear,
                  Growth_ProfitMargin, AssetTurnover_LYR, AssetTurnover_LYR_lastyear,EBITDA]
    for x in factorlist:
        df = pd.merge(df, x, how='outer', on='Symbol')
    df = df.dropna()

    df['Growth_ROA'] = df['ROA_OperatingProfit1_LYR'] - df['ROA_OperatingProfit1_LYR_lastyear'] #2
    #df['Growth_OCF'] = df['OperatingCashFlow'] - df['OperatingCashFlow_lastyear'] #4
    df['LTDebtToAsset'] = df['LTDebt']/df['TotalAsset']
    df['LTDebtToAsset_lastyear'] = df['LTDebt_lastyear']/df['TotalAsset_lastyear']
    df['Growth_LTDebtToAsset'] = df['LTDebtToAsset'] - df['LTDebtToAsset_lastyear']  # 5
    df['Growth_CurrentRatio'] = df['CurrentRatio'] - df['CurrentRatio_lastyear']  # 6
    df['Growth_TotalShares'] = df['TotalShares'] - df['TotalShares_lastyear']  # 7
    df['Growth_AssetTurnover_LYR'] = df['AssetTurnover_LYR'] - df['AssetTurnover_LYR_lastyear']  # 9
    df['Diff_OCF_R'] = df['OperatingCashFlow'] - df['EBITDA']
    df['Diff_CF_NI'] = 1 / df['PCF_LYR'] - 1 / df['PE_NetIncome2_LYR']  # 4

    return(df)

# ---F-score---

def Fscore(date):

    df = InitialData(date)

    def Score(df, reverse=False):
        score = []
        for each in np.array(df):
            if each == None:
                score.append(None)
            elif float(each) > 0:
                if reverse == True:
                    score.append(0)
                else:
                    score.append(1)
            else:
                if reverse == True:
                    score.append(1)
                else:
                    score.append(0)
        return score

    df['score1'] = Score(df['ROA_OperatingProfit1_LYR'])
    df['score2'] = Score(df['Growth_ROA'])
    df['score3'] = Score(df['OperatingCashFlow'])
    df['score4'] = Score(df['Diff_CF_NI'])
    df['score5'] = Score(df['Growth_LTDebtToAsset'], reverse=True)
    df['score6'] = Score(df['Growth_CurrentRatio'])
    df['score7'] = Score(df['Growth_TotalShares'], reverse=True)
    df['score8'] = Score(df['Growth_CAGR_ProfitMargin_OperatingProfit1_1Yr'])
    df['score9'] = Score(df['Growth_AssetTurnover_LYR'])

    df['Fscore'] = df['score1'] + df['score2'] + df['score3'] + df['score4'] + df['score5'] + df['score6'] + \
                   df['score7'] + df['score8'] + df['score9']

    df = df[['Symbol','score1','score2','score3','score4','score5','score6','score7','score8','score9','Fscore']]

    return df

# ---adjust date---

adjustdate = []
for year in range(2007, 2018):
    adjustdate.append('{}-05-09'.format(year))
adjustdate = StrtoDataTime(adjustdate)

# ---Trading Days---
#tradingdays = w.tdays("2000-01-01", "2019-09-30", "").Data[0]
tradingdays = pd.read_csv('tradingdays.csv', encoding='gbk', index_col=0)['0'].values
tradingdays = StrtoDataTime(list(tradingdays))

for i in range(len(adjustdate)):
    while adjustdate[i] not in tradingdays:
        adjustdate[i] -= oneday

for i in range(len(adjustdate)):
    adjustdate[i] += fifteenhours
print('---adjust date---', adjustdate)

# Define algorithm

def Initialize(api, context):
    pass

def HandledData(api, context, data, dt):
    pass


def OnDaily(api, context, dt):

    #
    if dt in adjustdate:
        print("---------------------------------Position Adjustment on " + str(dt),
              "---------------------------------")
        stock_fscore = Fscore(dt)
        stock_fscore.sort_values(by='Fscore', ascending=False, inplace=True)
        print('---Fscore of All Stocks---\n',stock_fscore)

        stock_fscore_point8 = stock_fscore[stock_fscore['score1'] == 0]
        #sum = stock_fscore_point8['FreeShares'].values.sum()
        #stock_fscore_point8['Weight'] = stock_fscore_point8['FreeShares'] / sum
        print('---Details of Group Selected---\n', stock_fscore_point8)
        dt2 = datetime.datetime.strftime(dt, "%Y-%m-%d")
        stock_fscore_point8.to_csv('stockselection_fscore_tryingfactors/score1_selected_{}.csv'.format(dt2))

        stock_code = stock_fscore_point8['Symbol'].values
        #stock_weight = stock_fscore_point8['Weight'].values

        symbols = []
        for i in range(len(stock_code)):
            symbols.append({"Symbol": stock_code[i]})

        api.Rebalance(symbols)
    else:
        pass

def OnWeekly(api, context, dt):
    pass

def OnMonthly(api, context, dt):
    pass


def OnMonthlyBegin(api, context, dt):
    pass

# ---交易日历---
tradingCalender = GetCalender("SH")

#
datetime1 = datetime.datetime(2007, 4, 28)
datetime2 = datetime.datetime(2018, 12, 30)

# ---回测参数设置---
simulatorParameters = SimulationParameters(datetime1=datetime1,
                                           datetime2=datetime2,
                                           trading_calendar=tradingCalender,
                                           data_frequency="monthly")
# ---交易环境：市场数据，业绩基准---
tradingEnvironment = TradingEnvironment(benchmark_symbol="000300.SH",
                                        database=database,
                                        realtimeView=realtime,
                                        trading_calendar=tradingCalender)

# ---构建策略---
strategy = TradingAlgorithm(name="TestStrategy",
                            initialize=Initialize,
                            handle_data=HandledData,
                            on_daily=OnDaily,
                            on_weekly=OnWeekly,
                            on_monthly=OnMonthly,
                            on_monthly_begin=OnMonthlyBegin,
                            analyze=StrategyEngine.Analyze,
                            simulator_parameters=simulatorParameters,
                            trading_environment=tradingEnvironment)

# ---策略中需要用到的参数---
context = {}

instruments = Gadget.FindListedInstrument(database, datetime1, datetime2)
context["Instruments"] = instruments

# ---开始回测---
statistics = strategy.Run(context=context)


# ---Print Statistics of Strategy---
statistics.to_csv('myoutput.csv', encoding='gbk')


